# TODO: Debug which test if logging can be user or simple print  
from django.utils import simplejson as json
import time

from datetime import timedelta, datetime 
import logging

__REVISION__= "$Revision: 757 $"
__REVISION_DATE__= "$Date: 2009-08-13 17:24:34 -0400 (Thu, 13 Aug 2009) $"

LOG_CRITICAL=logging.CRITICAL
LOG_ERROR=logging.ERROR
LOG_WARNING=logging.WARNING
LOG_INFO=logging.INFO
LOG_DEBUG=logging.DEBUG
LOG_LEVEL=LOG_INFO

# by default info
#print logging.getLogger().getEffectiveLevel(), logging.getLogger().level
myLogger=logging.getLogger()
myLogger.setLevel(LOG_LEVEL)

HTTP_OK = 200
HTTP_CREATED = 201
HTTP_UNAUTHORIZED = 401
HTTP_FORBIDDEN = 403
HTTP_GONE = 410
HTTP_ERROR = 500

# url making/parsing
SID_ID="__sid__"

TOK_ID="__tok__"
FUNCTION_ID="__f__"
OK_ID="__OK__"
OK_VALUE="OK"
SUBMIT_ID="__SUBMIT__"
SUBMIT_VALUE="Submit Request"
CANCEL_ID="__CANCEL__"
CANCEL_VALUE="CANCEL"
BACK_ID="__BACK__"
BACK_VALUE="BACK"

RESERVED_ID=(SID_ID,TOK_ID,FUNCTION_ID,OK_ID,CANCEL_ID,BACK_ID,SUBMIT_ID)

def getUserName(user=None):
    if user==None: return None
    if user.user_id(): return user.user_id()
    if user.nickname(): return user.nickname()
    if user.email(): return user.email()
    return None 

def createUrl(url, id=None):
    if id != None:
        sid=SID_ID+"="+str(id)
        if url.find("?") > -1: url += "&"+sid
        else: url += "?"+sid
    return  url

def createHref (url, text, id=None):
    return  "<a href=\"%s\">%s</a>" % (createUrl(url,id), text)

def fetch2table(q):
    s="<table border=1 >"
    for l in q:
        s += "<tr>"
        for c in dir(l):
            s += "<td>"
            s += str(c)
            s += "</td>"
        s += "</tr>"
    s += "</table>"

def writeFile (fn,ll):
    fp = open(fn, "w")
    try:
        for l in ll:
            fp.write(l)
            fp.write("\n")
    finally:
        fp.close()
writeListToFile=writeFile

def readfile (fn=None):
    """return a single string"""
    l = ""
    fp = open(fn, "rU")
    try:
        l = fp.read()
    finally:
        fp.close()
    return l

def appendFile (fn,ll):
    fp = open(fn, "a")
    try:
        for l in ll:
            fp.write(l)
            fp.write("\n")
    finally:
        fp.close()

def relative_string(elapsed):
    """ Takes a time delta and expresses it as a relative string """
    if elapsed < 2 * 60:
        result = "1 minute ago"
    elif elapsed < 60 * 60:
        result = "%d minutes ago" % (elapsed / 60)
    elif elapsed < 2 * 60 * 60:
        result = "1 hour ago"
    elif elapsed < 24 * 60 * 60:
        result = "%d hours ago" % (elapsed / (60 * 60))
    elif elapsed < 48 * 60 * 60:
        result = "1 day ago"
    else:
        result = "%d days ago" % (elapsed / (24 * 60 * 60))
    return result


def getProps(obj):
        ''' get the properties for a model/kind
        use getproperteis insead
        '''
        r={}
        for item in dir(obj):
            if not (item.startswith("_")):
                ite = getattr(obj, item)
                if isinstance(ite, unicode) or isinstance(ite, int) or isinstance(ite, float) or isinstance(ite, long) or isinstance(ite, str):
                    r[item]=str(ite)
                elif isinstance(ite, list):
                    r[item]=str("".join([str(i) for i in ite]))
                elif isinstance(ite, datetime):
                    r[item]=str(ite)
                else:
                    pass
        return r

def dump(o):
    import types
    s=o.__class__.__name__+":"
    for e in dir(o):
        if type(getattr(o, e))!=types.MethodType and not (e.startswith("__") and e.endswith("__")):
            s += " "+e # if not sequence... +"="+str(getattr(o, e))
    return s

def sendHeaders(self):
    def header(h,s):
        self.response.headers[h] = s
    ''' adapted from java? anyway, to be changed; just here to remind historical pb'''
    def headers_sent(): 
        return False
    if not headers_sent():
        header("Expires","Mon, 26 Jul 1997 05:00:00 GMT")
        header("Last-Modified", datetime.gmdate("D, d M Y H:i:s") + " GMT")
        header("Cache-Control","no-store, no-cache, must-revalidate") 
        header("Cache-Control","post-check=0, pre-check=0")
        header("Pragma","no-cache")

class Timer:
    def __init__(self,name=None):
        self.count=0
        if name==None: name="point "+str(self.count)
        self.name=name
        self.t0 = time.time()
        self.points=[]
        self.last=self.t0
    def point(self,name=None):
        self.count += 1
        if name==None: name="point "+str(self.count)
        t = time.time()
        self.points.append((name,str(round(t-self.last,4))+"s",str(round(t-self.t0,4))+"s" ))
        self.last=t
    def printAll(self,name=""):
        self.point("end")
        print
        for i in self.points:
            name,time,elapsed=i
            print str(i)

   
class renderModelinHTMLTable():
    '''
    Same than above with the object paragdyme
    Keep the object alive to render kind correctely
    Could use templating for example
    Could cache the title/header  
    '''
    def __init__(self, kind):
        self.kind=kind
    

def htmlTable(q,render=None):
    '''
    renderingFunction return the list of values to output for an object
    if a render method exists in the list of object returned (q), it is used
    '''
    # Model.kind()
    # Model.properties()
    if render==None and q and len(q)>0: render = getattr(q[0],"render",None)
    s="<p>"
    s+="<table border=1 cellspacing=0 cellpadding=2 >"
    if render: 
        s+="<CAPTION>"+render('title')+" ["+str(len(q))+"]</CAPTION>"
    else: 
        s+="<CAPTION>Number of rows:"+str(len(q))+"</CAPTION>"
    if q and len(q)>0:
#        s+=str(dir(q[0]))+"<br>\n"
        s += "<tr>"
        if render: 
            r = render('header')
        else:
            r=getProps(q[0]).iterkeys()
        for c in r:
            s += "<th>"+str(c)+"</th>"
        s += "</tr>"
    for se in q:
        s += "<tr>"
        if render: r = se.render()
        else: r = [getattr(se,p) for p in se.properties()]
        for v in r:
            s += "<td>"+str(v)+"</td>"
        s += "</tr>"
    s += "</table>"
    return s


def KindToHtmlTable(q,sel=None,sub=None):
    '''
    render a query result
    sel: select some columns
    sub: 
    '''
    s="<br />"
    if sub: s+= sub 
    #if q: s+=str(q[0].properties().keys())
    if q and len(q) > 0:
        s+="<table border=1 cellspacing=0 cellpadding=2 >"
        if sel:
            di=q[0].properties()
            li=[di[key] for key in sel] 
        else: 
            li=q[0].properties().values()
        s+="<CAPTION>"+q[0].kind()+" ["+str(len(q))+"]</CAPTION>"
        s += "<tr>"
        for prop in li:
                s += "<th>"+str(prop.name)+"</th>"
        s += "</tr>"
        for se in q:
            s += "<tr>"
            for prop in li:
                s += "<td>"+str(prop.get_value_for_datastore(se))+"</td>"
            s += "</tr>"
        s += "</table>"
    return s


def kindToTable(q,sel=None):
    '''
    create the table to put in the template
    sel: select some columns
    '''
    t=[]
    if q and len(q) > 0:
        if sel:
            di=q[0].properties()
            li=[di[key] for key in sel] 
        else: 
            li=q[0].properties().values()
        t.append([str(prop.name) for prop in li]) 
        for se in q:
            t.append([str(prop.get_value_for_datastore(se)) for prop in li]) 
    return t


def htmlPostTable(q,t=None):
    '''
    generate a form in a table layout to be posted by submit
    form key:value
    key=name
    value=(label,default,w|r)
    '''
    s="<p>"
    s+="<table border=1 cellspacing=0 cellpadding=2 >"
    for tuple in q:
        k,l,a,siz = (len(tuple) == 4) and tuple or tuple+(30,) # force siz=30 if not provided
        s += "<tr>"
        s += "<td>"+l+"</td>"
        val = t and getattr(t,k) or "" 
        if a=="w":
            s += "<td><input type='text' name='%s' value='%s' size=%d></td>" % (k,val,siz)
        else:
            s += "<td><div name='%s'>%s</div></td>" % (k,val)
        s += "</tr>"
    s += "</table>"
    return s


class PageHandler():
    '''handle an gslq query including back and forth, jump to start/end'''
  
    def __init__ (self,q,n=20):
        '''
        q query: any kind works
        '''
        #self.entity=entity
        self.q = q
        self.limit=n
        self.offset=0
        self.count=self.q.count()
        self.n = n

    def get(self):
        if self.offset + self.limit > self.count: 
            self.offset = max (0, self.count - self.limit)
        return self.q.fetch(self.limit,self.offset)

    def start(self):
        self.offset = 0 
        return self.q.fetch(self.limit,self.offset)

    def end(self):
        self.offset = max (0, self.count - self.limit) 
        return self.q.fetch(self.limit,self.offset)

    def backward(self):
        self.offset -= self.n 
        if self.offset < 0: self.offset = 0
        return self.q.fetch(self.limit,self.offset)

    def forward(self):
        self.offset += self.n 
        if self.offset + self.limit > self.count: 
            self.offset = max (0, self.count - self.limit)
        return self.q.fetch(self.limit,self.offset)

    def getState(self):
        return str(self.offset) + "-" + str(min(self.count, self.offset+self.n))
  
    def dump(self):
        return json.dumps(self)


def accepts(*types):
    def check_accepts(f):
        #assert (len(types) == f.func_code.co_argcount) , ("number of arguments of <%s> incorrect" % (f.__name__))
        def new_f(*args, **kwds):
            for (a, t) in zip(args, types):
                assert isinstance(a, t), \
                       "arg %r does not match %s" % (a,t)
            return f(*args, **kwds)
        new_f.func_name = f.func_name
        return new_f
    return check_accepts

def returns(rtype):
    def check_returns(f):
        def new_f(*args, **kwds):
            result = f(*args, **kwds)
            assert isinstance(result, rtype), \
                   "return value %r does not match %s" % (result,rtype)
            return result
        new_f.func_name = f.func_name
        return new_f
    return check_returns

@accepts(int, (int,float))
@returns((int,float))
def func(arg1,arg2):
    return arg1 * arg2


def getDT(dt):
    '''transform a dt in a triplet'''
    return [dt.days,dt.seconds,dt.microseconds]

def setDT(triplet):
    '''transform a triplet in dt'''
    if triplet==[]: return None
    return timedelta(days=triplet[0],seconds=triplet[1],microseconds=triplet[2])


if __name__ == "__main__":
    import doctest
    doctest.testmod(verbose=True)   # automatically validate the embedded tests
    print "CRITICAL",myLogger.isEnabledFor(LOG_CRITICAL)
    print "ERROR",myLogger.isEnabledFor(LOG_ERROR)
    print "WARNING",myLogger.isEnabledFor(LOG_WARNING)
    print "INFO",myLogger.isEnabledFor(LOG_INFO)
    print "DEBUG",myLogger.isEnabledFor(LOG_DEBUG)
